package com.doeis.tools;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

public class BeanUtils {
	private static Object[] objects = new Object[0];
	private static HashMap<Class<?>, Method[]> beanMethodCache = new HashMap<Class<?>, Method[]>();
	private static HashMap<Class<?>, HashMap<String, Method>> fromBeanMethodCache = new HashMap<Class<?>, HashMap<String, Method>>();
	private static HashMap<Class<?>, HashMap<String, PropertyDescriptor>> fromBeanPropertyDescriptorCache = new HashMap<Class<?>, HashMap<String, PropertyDescriptor>>();

	private static final Logger log = Logger.getLogger(BeanUtils.class);

	public static Map<String, Class<?>> describe(Object obj) {
		Map<String, Class<?>> pros = new HashMap<String, Class<?>>();
		try {
			Class<?> clazz = obj.getClass();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				if ("serialVersionUID".equals(field.getName()))
					continue;
				pros.put(field.getName(), field.getType());
			}
			if (clazz.getSuperclass() != null)
				describe(pros, clazz.getSuperclass());
		} catch (Exception e) {
			log.warn(e.getMessage(), e);
		}
		return pros;
	}

	public static void setProperty(Object obj, String field, Object fieldValue) {
		Class<?> paramClazz = null;
		if (obj == null) {
			return;
		}
		Method method = null;
		try {
			Class<?> clazz = obj.getClass();
			Map prosMap = propertyDescriptorsWithFiles(clazz);
			PropertyDescriptor propertyDescriptor = (PropertyDescriptor) prosMap.get(field);
			if (propertyDescriptor == null) {
				return;
			}
			if (fieldValue == null) {
				return;
			}
			method = propertyDescriptor.getWriteMethod();
			Class[] clazzes = method.getParameterTypes();
			if ((clazzes != null) && (clazzes.length == 1)) {
				paramClazz = clazzes[0];
				fieldValue = changeClassType(fieldValue, paramClazz);
				if (fieldValue != null)
					method.invoke(obj, new Object[] { fieldValue });
			}
		} catch (Exception e) {
			log.warn(
					"field=" + field + "; paramClazz=" + paramClazz + " fieldValue=" + fieldValue + "; " + " methodParameterTypes="
							+ method.getParameterTypes() + "; \r\n msg=" + e.getMessage(), e);
		}
	}

	public static Object getProperty(Object obj, String field) {
		try {
			Map prosMap = propertyDescriptorsWithFiles(obj.getClass());
			PropertyDescriptor propertyDescriptor = (PropertyDescriptor) prosMap.get(field);
			try {
				if (propertyDescriptor != null) {
					Method method = propertyDescriptor.getReadMethod();
					return method.invoke(obj, objects);
				}
			} catch (Exception localException1) {
			}
		} catch (Exception e) {
			log.warn("field:" + field + "; msg:" + e.getMessage(), e);
		}
		return null;
	}

	public static void populate(Object obj, Map<Object, Object> properties) {
		String methodName = null;
		String proName = null;
		Object mapValue = null;
		Class paramClazz = null;
		try {
			Class clazz = obj.getClass();
			Method[] methods = clazz.getMethods();
			for (Method m : methods) {
				methodName = m.getName();
				if ((methodName.startsWith("set")) && (m.getParameterTypes().length > 0)) {
					paramClazz = m.getParameterTypes()[0];
					if (methodName.length() == 4)
						proName = methodName.substring(3).toLowerCase();
					else {
						proName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
					}
					mapValue = properties.get(proName);
					if (mapValue != null) {
						mapValue = changeClassType(mapValue, paramClazz);
						if (mapValue != null)
							m.invoke(obj, new Object[] { mapValue });
					}
				}
			}
		} catch (Exception e) {
			log.warn(e.getMessage() + ", " + "methodName-->" + methodName, e);
		}
	}

	private static Object changeClassType(Object value, Class<?> clazz) {
		Class valueClass = value.getClass();
		if (valueClass.equals(clazz)) {
			return value;
		}

		if (StringUtils.isNotBlank(value.toString())) {
			if (isPrimitiveOrEntity(clazz))
				value = primitiveObj(value.toString(), clazz);
			else if (isOutPrimitiveEntity(clazz))
				value = outPrimitiveObj(value, clazz);
		} else {
			value = null;
		}

		return value;
	}

	public static <T> T copyProperties(Class<T> beanClass, Object fromBean) {
		try {
			T bean = beanClass.newInstance();
			Class formBeanClass = fromBean.getClass();

			Method[] beanMethods = (Method[]) beanMethodCache.get(beanClass);
			if (beanMethods == null) {
				beanMethods = beanClass.getMethods();
				beanMethodCache.put(beanClass, beanMethods);
			}

			HashMap fromBeanMethods = (HashMap) fromBeanMethodCache.get(formBeanClass);
			if (fromBeanMethods == null) {
				fromBeanMethods = new HashMap();
				Method[] methods = formBeanClass.getMethods();
				String getMethodName = null;
				for (Method method : methods) {
					getMethodName = method.getName();
					if ((getMethodName.startsWith("get")) || (getMethodName.startsWith("is")))
						fromBeanMethods.put(getMethodName, method);
				}
				fromBeanMethodCache.put(formBeanClass, fromBeanMethods);
			}

			String methodName = null;
			String getMethodName = null;
			Class[] paramsType = null;
			Class paramType = null;
			Object value = null;
			for (Method method : beanMethods) {
				methodName = method.getName();
				if (methodName.startsWith("set")) {
					paramsType = method.getParameterTypes();
					if (paramsType.length != 1) {
						continue;
					}
					paramType = paramsType[0];
					if ("boolean".equals(paramType.toString()))
						getMethodName = "is" + methodName.substring(3);
					else {
						getMethodName = "get" + methodName.substring(3);
					}
					Method formBeanGetMethod = (Method) fromBeanMethods.get(getMethodName);
					if ((formBeanGetMethod == null) || (!paramType.equals(formBeanGetMethod.getReturnType())))
						continue;
					value = formBeanGetMethod.invoke(fromBean, objects);
					if (value != null) {
						method.invoke(bean, new Object[] { value });
					}
				}

			}
			return bean;
		} catch (Exception e) {
			log.warn(e.getMessage(), e);
		}
		return null;
	}

	private static void describe(Map<String, Class<?>> pros, Class<?> superClazz) {
		Field[] fields = superClazz.getDeclaredFields();
		for (Field field : fields) {
			if ("serialVersionUID".equals(field.getName()))
				continue;
			pros.put(field.getName(), field.getType());
		}
		if (superClazz.getSuperclass() != null)
			describe(pros, superClazz.getSuperclass());
	}

	public static Object primitiveObj(String value, Class<?> clazz) {
		if ((clazz.isInstance(Integer.TYPE)) || ("int".equals(clazz.getName())) || (Integer.class.equals(clazz))) {
			if ("null".equals(value)) {
				return null;
			}

			if (value.indexOf(".") > 0) {
				return Integer.valueOf(Float.valueOf(value).intValue());
			}
			return Integer.valueOf(value);
		}
		if ((clazz.isInstance(Boolean.TYPE)) || ("boolean".equals(clazz.getName())) || (Boolean.class.equals(clazz))) {
			if (("on".equalsIgnoreCase(value)) || ("1".equals(value)))
				return Boolean.TRUE;
			if (("off".equalsIgnoreCase(value)) || ("0".equals(value))) {
				return Boolean.FALSE;
			}
			return Boolean.valueOf(value);
		}
		if ((clazz.isInstance(Long.TYPE)) || ("long".equals(clazz.getName())) || (Long.class.equals(clazz))) {
			if ("null".equals(value)) {
				return null;
			}

			return Long.valueOf(value);
		}
		if ((clazz.isInstance(Float.TYPE)) || ("float".equals(clazz.getName())) || (Float.class.equals(clazz))) {
			if ("null".equals(value)) {
				return null;
			}
			return Float.valueOf(value);
		}
		if ((clazz.isInstance(Double.TYPE)) || ("double".equals(clazz.getName())) || (Double.class.equals(clazz))) {
			if ("null".equals(value)) {
				return null;
			}
			return Double.valueOf(value);
		}
		if ((clazz.isInstance(Byte.TYPE)) || ("byte".equals(clazz.getName())) || (Byte.class.equals(clazz)))
			return Byte.valueOf(value);
		if ((clazz.isInstance(Character.TYPE)) || ("char".equals(clazz.getName())) || (Character.class.equals(clazz)))
			return Character.valueOf(value.charAt(0));
		if ((clazz.isInstance(Short.TYPE)) || ("short".equals(clazz.getName())) || (Short.class.equals(clazz))) {
			return Short.valueOf(value);
		}
		return null;
	}

	public static Object outPrimitiveObj(Object value, Class<?> clazz) {
		try {
			Class valueClass = value.getClass();
			if (java.util.Date.class.equals(valueClass)) {
				java.util.Date d = (java.util.Date) value;
				long l = d.getTime();
				if (java.sql.Date.class.equals(clazz))
					return new java.sql.Date(l);
				if (java.util.Date.class.equals(clazz))
					return value;
				if (Timestamp.class.equals(clazz))
					return new Timestamp(l);
				if (Time.class.equals(clazz))
					return new Time(l);
				if (BigDecimal.class.equals(clazz))
					return BigDecimal.valueOf(l);
			} else {
				String v = value.toString();
				if (java.sql.Date.class.equals(clazz))
					return java.sql.Date.valueOf(v);
				if (java.util.Date.class.equals(clazz)) {
					SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
					return fmt.parse(v);
				}
				if (Timestamp.class.equals(clazz))
					return Timestamp.valueOf(v);
				if (Time.class.equals(clazz))
					return Time.valueOf(v);
				if (BigDecimal.class.equals(clazz))
					return BigDecimal.valueOf(Double.valueOf(v).doubleValue());
			}
		} catch (Exception e) {
			log.warn(value + " cast to " + clazz + " error. ");
		}
		return null;
	}

	public static boolean isPrimitiveOrEntity(Class<?> clazz) {
		if (clazz.isPrimitive()) {
			return true;
		}
		return (Integer.class.equals(clazz)) || (Boolean.class.equals(clazz)) || (Long.class.equals(clazz)) || (Float.class.equals(clazz))
				|| (Double.class.equals(clazz)) || (Character.class.equals(clazz)) || (Short.class.equals(clazz)) || (Byte.class.equals(clazz));
	}

	public static boolean isOutPrimitiveEntity(Class<?> clazz) {
		return (java.sql.Date.class.equals(clazz)) || (java.util.Date.class.equals(clazz)) || (Timestamp.class.equals(clazz))
				|| (Time.class.equals(clazz)) || (BigDecimal.class.equals(clazz));
	}

	public static PropertyDescriptor[] propertyDescriptors(Class<?> c) throws Exception {
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(c);
		} catch (IntrospectionException e) {
			throw new Exception("Bean introspection failed: " + e.getMessage());
		}
		return beanInfo.getPropertyDescriptors();
	}

	public static Map<String, PropertyDescriptor> propertyDescriptorsWithFiles(Class<?> c) throws Exception {
		HashMap map = null;
		try {
			map = (HashMap) fromBeanPropertyDescriptorCache.get(c);
			if (map == null) {
				String propName = null;
				PropertyDescriptor[] pros = propertyDescriptors(c);
				map = new HashMap(pros.length);
				for (PropertyDescriptor p : pros) {
					propName = p.getName();
					if ("class".equalsIgnoreCase(propName))
						continue;
					map.put(propName, p);
				}
				fromBeanPropertyDescriptorCache.put(c, map);
			}
		} catch (IntrospectionException e) {
			throw new Exception("Bean introspection failed: " + e.getMessage());
		}
		return map;
	}

	public static void main(String[] args) {
		Float a = new Float(123.0F);
		float b = 12.0F;
		Object d = Float.valueOf(b);
		System.out.println(b);
	}
}